package com.ruoyi.rva.service.impl;

import com.github.pagehelper.PageHelper;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.rva.domain.*;
import com.ruoyi.rva.extension.RvaDeleteInterceptor;
import com.ruoyi.rva.extension.RvaFormSubmitInterceptor;
import com.ruoyi.rva.extension.RvaListSQLInterceptor;
import com.ruoyi.rva.mapper.*;
import com.ruoyi.rva.service.IRvaViewService;
import com.ruoyi.rva.util.RvaConstants;
import com.ruoyi.rva.util.RvaJsonUtils;
import com.ruoyi.rva.util.RvaUtils;
import com.ruoyi.rva.util.RvaVelocityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 视图Service业务层处理
 * 
 * @author jiaodacailei
 * @date 2021-09-02
 */
@Service
@Transactional
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class RvaViewServiceImpl implements IRvaViewService 
{
    private final RvaDataMapper rvaDataMapper;

    private final RvaViewMapper rvaViewMapper;

    private final RvaObjectMapper rvaObjectMapper;

    private final RvaPropertyMapper rvaPropertyMapper;

    private final RvaRelationMapper rvaRelationMapper;

    @Autowired
    private RvaVelocityUtils velocityUtils;

    @Override
    public RvaView selectRvaViewById(String viewId, Map<String, Object> req) {
        return rvaViewMapper.selectRvaViewById(viewId);
    }

    @Override
    public RvaMap selectListViewData(String listId, String searchId, RvaMap req) {
        List<RvaMap<String, Object>> listData = selectList(listId, searchId, req);
        RvaView list = rvaViewMapper.selectRvaViewById(listId);
        return new RvaMap("viewData", list).rvaPut("listData", listData);
    }

    @Override
    public List<RvaMap<String, Object>> selectList(String listId, String searchId, RvaMap req, String... wheres) {
        RvaView list = rvaViewMapper.selectRvaViewById(listId);
        RvaView search = RvaUtils.isEmpty(searchId) ? null : rvaViewMapper.selectRvaViewById(searchId);
        RvaObject object = rvaObjectMapper.selectRvaObjectById(list.getObjId());
        Optional<RvaListSQLInterceptor> sqlInterceptor = RvaUtils.getBean(RvaListSQLInterceptor.BEAN_FREFIX + list.getId());
        if (RvaUtils.isNotEmpty(list.getLoadSql())) {
            RvaSQL rvaSQL = new RvaSQL();
            sqlInterceptor.ifPresent(e -> e.preHandle(rvaSQL, object, list, search, req));
            addWhere(req, object, search, rvaSQL);
            rvaSQL.where(wheres);
            addOrderBys(req, object, list, rvaSQL);
            sqlInterceptor.ifPresent(e -> e.postHandle(rvaSQL, object, list, search, req));
            String sql = velocityUtils.parseWithLoginUser(list.getLoadSql(), req) + " and " + rvaSQL.getWhere() + " order by " + rvaSQL.getOrderBys();
            log.info("selectList-loadSql:\n" + sql);
            return rvaDataMapper.selectList(sql);
        }
        // 处理load_before_sql
        loadBeforeSql(req, list.getLoadBeforeSql());
        RvaSQL sql = new RvaSQL();
        sqlInterceptor.ifPresent(e -> e.preHandle(sql, object, list, search, req));
        sql.from(object.getId(), object.getNo());
        sql.where(wheres);
        String keyPropId = null;
        String namePropId = null;
        // 处理列表视图，生成查询字段和部分where条件
        for (RvaViewproperty viewproperty : list.getProperties()) {
            // 获取req中的值，用于where
            String value = req.getString(viewproperty.getId());
            if (viewproperty.getPropId() != null) {
                RvaProperty property = rvaPropertyMapper.selectRvaPropertyById(viewproperty.getPropId());
                sql.select(object.getNo(), property.getName(), viewproperty.getPropSubId(), viewproperty.getId());
                if (viewproperty.getRelationId() == null) {
                    addWhere(sql, object.getNo(), property.getName(), value);
                    if (object.isPrimaryKey(property.getName())) {
                        keyPropId = viewproperty.getId();
                    }
                    if (object.isNameProp(property.getName())) {
                        namePropId = viewproperty.getId();
                    }
                }
            }
            if (viewproperty.getRelationId() != null) {
                RvaRelation relation = rvaRelationMapper.selectRvaRelationById(viewproperty.getRelationId());
                if (relation.canLeftJoin()) {
                    RvaRelationitem relationitem = relation.getItems().get(0);
                    RvaObject relatedObject = rvaObjectMapper.selectRvaObjectById(relationitem.getRelatedObjId());
                    String propName = object.getProperty(relationitem.getPropId()).getName();
                    String relatedPropName = relatedObject.getProperty(relationitem.getRelatedPropId()).getName();
                    sql.leftJoin(relatedObject.getId(), relation.getId(), String.format("%s.%s = %s.%s", object.getNo(), propName, relation.getId(), relatedPropName));
                    addWhere(sql, relation.getId(), propName, value);
                }
            }
            sql.selectEmpty(viewproperty.getId());
        }
        // 处理对象属性，生成where条件
        object.getProperties().forEach(rvaProperty -> {
            addWhere(sql, object.getNo(), rvaProperty.getName(), req.getString(rvaProperty.getId()));
        });
        // 处理关联对象属性，生成where条件
        object.getRelations().forEach(relation -> {
            RvaRelationitem relationitem = relation.getItems().get(0);
            RvaObject relatedObject = rvaObjectMapper.selectRvaObjectById(relationitem.getRelatedObjId());
            relatedObject.getProperties().forEach(rvaProperty -> {
                addWhere(sql, relation.getId(), rvaProperty.getName(), req.getString(rvaProperty.getId()));
            });
        });
        // 处理查询视图，生成where条件
        if (search != null) {
            addWhere(req, object, search, sql);
        }
        // 处理load_where
        if (RvaUtils.isNotEmpty(list.getLoadWhere())) {
            sql.where(velocityUtils.parseWithLoginUser(list.getLoadWhere(), req));
        }
        // 处理排序
        addOrderBys(req, object, list, sql);
        sqlInterceptor.ifPresent(e -> e.postHandle(sql, object, list, search, req));
        log.info("selectList:\n" + sql);
        // 设置分页信息
        PageHelper.startPage(req.getInt("pageNum", 1), req.getInt("pageSize", 10));
        // rvaSystemService.startPage();
        List<RvaMap<String, Object>> selectList = rvaDataMapper.selectList(sql.toString());
        // 处理load_after_sql
        loadAfterSql(req, list.getLoadAfterSql(), selectList);
        // 后续处理，给每行数据加上id和name属性ID
        for (RvaMap<String, Object> map : selectList) {
            map.put(RvaConstants.PROP_KEY_ID, keyPropId);
            map.put(RvaConstants.PROP_KEY_VALUE, map.get(keyPropId));
            map.put(RvaConstants.PROP_NAME_ID, namePropId);
            map.put(RvaConstants.PROP_NAME_VALUE, map.get(namePropId));
            // 处理inner按钮
            list.getProperties().forEach(prop -> {
                if (RvaViewproperty.TYPE_BUTTON.equals(prop.getType()) && RvaUtils.isNotEmpty(prop.getListButtons())) {
                    String[] buttons = prop.getListButtons().split(",");
                    List<RvaViewbutton> wanted = new ArrayList<>();
                    for (String buttonId : buttons) {
                        RvaViewbutton button = RvaUtils.cloneBySetter(list.getButton(buttonId), RvaViewbutton.class);
                        RvaMap velocityContext = new RvaMap<>(map).rvaPutAll(req);
                        if (velocityUtils.validateWithLoginUser(button.getShowIf(), velocityContext)) {
                            wanted.add(button);
                            button.setType(velocityUtils.parseWithLoginUser(button.getType(), velocityContext));
                            button.setActionUrl(velocityUtils.parseWithLoginUser(button.getActionUrl(), velocityContext));
                            button.setActionDialogAppId(velocityUtils.parseWithLoginUser(button.getActionDialogAppId(), velocityContext));
                            button.setActionDialogViewId(velocityUtils.parseWithLoginUser(button.getActionDialogViewId(), velocityContext));
                        }
                    }
                    map.put(prop.getId(), wanted);
                }
            });
        }
        return selectList;
    }

    private void addWhere(RvaSQL sql, String tableAlias, String column, String value) {
        if (value != null) {
            String where = "%s.%s = '%s'";
            if (value.indexOf('%') >= 0) {
                where = "%s.%s like '%s'";
            }
            sql.where(String.format(where, tableAlias, column, value));
        }
    }

    private void addOrderBy(RvaSQL sql, String tableAlias, String column, String value) {
        if ("ascending".equals(value)) {
            value = "asc";
        } else if ("descending".equals(value)) {
            value = "desc";
        }
        String orderby = "%s.%s %s";
        sql.orderBy(String.format(orderby, tableAlias, column, value));
    }

    private void addWhere(RvaMap req, RvaObject object, RvaView search, RvaSQL sql) {
        for (RvaViewproperty viewproperty : search.getProperties()) {
            // 获取req中的值，用于where
            String value = req.getString(viewproperty.getId());
            if (RvaUtils.isEmpty(value)) {
                continue;
            }
            String expression = viewproperty.getSearchExpression();
            if (RvaViewproperty.SEARCH_EQUAL.equals(viewproperty.getSearchType())) {
                expression = "${column} = '${value}'";
            } else if (RvaViewproperty.SEARCH_LIKE.equals(viewproperty.getSearchType())) {
                expression = "${column} like '%${value}%'";
            }
            //
            RvaMap<String, Object> velocityData = new RvaMap<>("value", value);
            if (viewproperty.getPropId() != null) {
                RvaProperty property = rvaPropertyMapper.selectRvaPropertyById(viewproperty.getPropId());
                if (viewproperty.getRelationId() == null) {
                    velocityData.put("column", object.getNo() + "." + getColumn(viewproperty, property));
                } else {
                    RvaRelation relation = rvaRelationMapper.selectRvaRelationById(viewproperty.getRelationId());
                    if (relation.canLeftJoin()) {
                        velocityData.put("column", relation.getId() + "." + getColumn(viewproperty, property));
                    }
                }
            }
            String parse = velocityUtils.parseWithLoginUser(expression, velocityData);
            sql.where(parse);
        }
    }

    private void addOrderBys(RvaMap req, RvaObject object, RvaView list, RvaSQL sql) {
        List<Map> orderBys = req.getList(RvaConstants.PARAM_ORDER_BY);
        if (orderBys.size() == 0) {
            list.getProperties().stream().filter(p -> p.getListOrderIdx() >= 0).sorted(new Comparator<RvaViewproperty>() {
                @Override
                public int compare(RvaViewproperty o1, RvaViewproperty o2) {
                    return o1.getListOrderIdx() - o2.getListOrderIdx();
                }
            }).forEach(p -> {
                Map orderBy = new HashMap();
                orderBys.add(orderBy);
                orderBy.put("prop", p.getId());
                orderBy.put("order", p.getListOrderType().equals(RvaViewproperty.LIST_ORDER_ASC) ? "ascending" : "descending");
            });
        }
        addOrderBys(object, list, sql, orderBys);
    }

    private void addOrderBys(RvaObject object, RvaView list, RvaSQL sql, List<Map> orderBys) {
        for (Map orderBy : orderBys) {
            String prop = orderBy.get("prop").toString();
            Object order = orderBy.get("order");
            if (RvaUtils.isEmpty(order)) {
                continue;
            }
            RvaViewproperty viewproperty = list.getProperty(prop);
            if (viewproperty.getPropId() != null) {
                RvaProperty property = rvaPropertyMapper.selectRvaPropertyById(viewproperty.getPropId());
                if (viewproperty.getRelationId() == null) {
                    addOrderBy(sql, object.getNo(), getColumn(viewproperty, property), order.toString());
                } else {
                    RvaRelation relation = rvaRelationMapper.selectRvaRelationById(viewproperty.getRelationId());
                    if (relation.canLeftJoin()) {
                        addOrderBy(sql, relation.getId(), getColumn(viewproperty, property), order.toString());
                    }
                }
            }
        }
    }

    private String getColumn(RvaViewproperty viewproperty, RvaProperty property) {
        String col = property.getName();
        if (RvaUtils.isNotEmpty(viewproperty.getPropSubId())) {
            col = String.format("%s->>'$.%s'", col, viewproperty.getPropSubId());
        }
        return col;
    }

    private void loadBeforeSql(RvaMap req, String loadBeforeSql) {
        if (RvaUtils.isEmpty(loadBeforeSql)) {
            return;
        }
        List<String> sqls = parseSqls(loadBeforeSql);
        for (String s : sqls) {
            String sql = velocityUtils.parseWithLoginUser(s, req);
            List<RvaMap<String, Object>> beforeList = rvaDataMapper.selectList(sql);
            for (RvaMap<String, Object> row : beforeList) {
                req.putAll(row);
            }
        }
    }

    private void loadAfterSql(RvaMap req, String loadAfterSql, List<RvaMap<String, Object>> selectList) {
        if (RvaUtils.isEmpty(loadAfterSql)) {
            return;
        }
        List<String> sqls = parseSqls(loadAfterSql);
        for (RvaMap<String, Object> row : selectList) {
            for (String sql : sqls) {
                String s = velocityUtils.parseWithLoginUser(sql, new RvaMap<>(req).rvaPutAll(row));
                List<RvaMap<String, Object>> rvaMaps = rvaDataMapper.selectList(s);
                for (RvaMap<String, Object> r : rvaMaps) {
                    row.putAll(r);
                }
            }
        }
    }

    private List<String> parseSqls(String loadAfterSql) {
        List<String> sqls = new ArrayList<>();
        if (RvaUtils.isEmpty(loadAfterSql)) {
            return sqls;
        }
        if (loadAfterSql.indexOf('[') >= 0) {
            sqls = RvaJsonUtils.readAsList(loadAfterSql, String.class);
        } else {
            sqls.add(loadAfterSql);
        }
        return sqls;
    }

    @Override
    public RvaMap selectCreateViewData(String viewId, RvaMap req) {
        RvaView view = rvaViewMapper.selectRvaViewById(viewId);
        loadBeforeSql(req, view.getLoadBeforeSql());
        // List<Map> selection = req.parseList("selection");
        RvaMap<String, Object> formData = new RvaMap<>();
        view.getProperties().forEach(p -> {
            String val = null;
            if (RvaUtils.isNotEmpty(p.getPropId())) {
                val = req.getString(p.getPropId());
            }
            if (req.isNotEmpty(p.getId())) {
                val = req.getString(p.getId());
            }
            val = velocityUtils.parseWithLoginUser(val, req);
            val = getValueByInit(val, req, p);
            formData.put(p.getId(), val);
            setFormRelatedCrudData(formData, p, req, true, false);
            setFormMultipleValues(formData, p, val);
        });
        List<RvaMap<String, Object>> formDataList = new ArrayList<>();
        formDataList.add(formData);
        loadAfterSql(req, view.getLoadAfterSql(), formDataList);
        return new RvaMap("viewData", view).rvaPut("formData", formData);
    }

    @Override
    public RvaMap selectCloneViewData(String viewId, RvaMap req) {
        return selectUpdateViewData(viewId, req, true, true);
    }

    /**
     *
     * @param formData
     * @param p
     * @param request
     * @param create
     */
    private void setFormRelatedCrudData (RvaMap<String, Object> formData, RvaViewproperty p, RvaMap request, Boolean create, Boolean clone) {
        if (RvaViewproperty.TYPE_CRUD.equals(p.getType())) {
            RvaMap req = new RvaMap(formData).rvaPutAll(request);
            List crudData = new ArrayList<>();
            RvaApp relatedApp = rvaAppMapper.selectRvaAppById(p.getFormRelatedCrud());
            if (!create) {
                RvaMap updateViewData = selectUpdateViewData(clone ? relatedApp.getCreateId() : relatedApp.getUpdateId(), req, clone, false);
                p.getRelatedCrudViewData().put(RvaView.FORM_UPDATE, updateViewData);
                crudData = (List) updateViewData.get("formDataList");
            }
            RvaMap createViewData = selectCreateViewData(relatedApp.getCreateId(), req);
            p.getRelatedCrudViewData().put(RvaView.FORM_CREATE, createViewData);
            Map createFormData = (Map)createViewData.get("formData");
            createFormData.put(RvaView.FORM_CREATE, true);
            crudData.add(createFormData);
            if (clone) {
                crudData.forEach(data -> ((Map)data).put(RvaView.FORM_CREATE, true));
            }
            formData.put(p.getId(), crudData);
        }
    }

    private void setFormMultipleValues(RvaMap<String, Object> formData, RvaViewproperty p, String val) {
        if (p.hasMultipleValues()) {
            List<String> vals = new ArrayList<>();
            if (RvaUtils.isNotEmpty(val)) {
                for (String s : val.split(",")) {
                    vals.add(s);
                }
            }
            formData.put(p.getId(), vals);
        }
    }

    /**
     * form加载时，根据FormInitValue替换值
     * @param req
     * @param p
     * @return
     */
    private String getValueByInit(String val, RvaMap req, RvaViewproperty p) {
        if (RvaUtils.isNotEmpty(p.getFormInitValue())) {
            if (RvaUtils.isNotEmpty(val)) {
                if ("Y".equals(p.getFormInitReplace())) {
                    val = velocityUtils.parseWithLoginUser(p.getFormInitValue(), req);
                }
            } else {
                if ("Y".equals(p.getFormInitReplaceEmpty())) {
                    val = velocityUtils.parseWithLoginUser(p.getFormInitValue(), req);
                }
            }
        }
        return val;
    }

    @Override
    public void submitCreateView(String viewId, RvaMap req) {
        RvaView create = rvaViewMapper.selectRvaViewById(viewId);
        RvaObject object = rvaObjectMapper.selectRvaObjectById(create.getObjId());// create.getObjId()为none时，object为null
        RvaMap<String, Object> fieldValues = new RvaMap<>();
        Optional<RvaFormSubmitInterceptor> bean = RvaUtils.getBean(RvaFormSubmitInterceptor.BEAN_FREFIX + create.getId());
        bean.ifPresent(rvaFormSubmitInterceptor -> {
            rvaFormSubmitInterceptor.preHandle(fieldValues, object, create, req);
        });
        executeSubmitSql(req, create.getFormSubmitBeforeSql());
        create.getProperties().forEach(p -> {
            if ("Y".equals(p.getFormRequired()) && req.isEmpty(p.getId())) {
                throw new CustomException(String.format("【%s】为必需！", p.getName()));
            }
            if (object == null) {
                return;
            }
            RvaProperty prop = object.getProperty(p.getPropId());
            if (prop == null) {
                return;
            }
            setFieldValues(req, fieldValues, p, prop);
        });
        if (object != null) {
            checkUnique(object, fieldValues, true);
            rvaDataMapper.insert(object.getId(), fieldValues);
            updateIndexValue(req, object);
        }
        processCrud(req, create);
        executeSubmitSql(req, create.getFormSubmitAfterSql());
        bean.ifPresent(rvaFormSubmitInterceptor -> {
            rvaFormSubmitInterceptor.postHandle(fieldValues, object, create, req);
        });
    }

    private void executeSubmitSql(RvaMap req, String submitSql) {
        parseSqls(submitSql).forEach(sql -> {
            sql = velocityUtils.parseWithLoginUser(sql, req);
            rvaDataMapper.update(sql);
        });
    }

    private void updateIndexValue(RvaMap req, RvaObject object) {
        if (object.hasPropIndex()) {
            this.updateIndex(object.getRequestKeyValue(req), object.getRequestIndexValue(req), object, object.getRequestIndexData(req));
        }
    }

    // object.uniques == [["", ""],[""]]
    private void checkUnique (RvaObject object, RvaMap<String, Object> fieldValues, Boolean insert) {
        String uniques = object.getUniques();
        if (RvaUtils.isEmpty(uniques)) {
            return;
        }
        List<List> uniqueList = RvaJsonUtils.readAsList(uniques, List.class);
        for (List unique : uniqueList) {
            RvaSQL sql = new RvaSQL();
            sql.select("count(*)");
            sql.from(object.getId());
            for (Object prop : unique) {
                Object val = fieldValues.get(prop);
                if (val == null) {
                    sql.where(prop.toString() + " is null");
                } else {
                    sql.where(String.format(prop + " = '%s'", val));
                }
            }
            if (!insert) {
                for (String key : object.getPropNameKey().split(",")) {
                    sql.where(String.format("%s != '%s'", key, fieldValues.get(key)));
                }
            }
            Long count = rvaDataMapper.selectLong(sql.toString());
            if (count > 0) {
                throw new CustomException("违反唯一性约束：" + unique);
            }
        }
    }

    private void processCrud(RvaMap req, RvaView create) {
        create.getProperties().forEach(p -> {
            if (RvaViewproperty.TYPE_CRUD.equals(p.getType())) {
                List<Map> list = req.getList(p.getId());
                RvaApp relatedApp = rvaAppMapper.selectRvaAppById(p.getFormRelatedCrud());
                for (Map r : list) {
                    if (r.containsKey("create")) {
                        submitCreateView(relatedApp.getCreateId(), new RvaMap(req).rvaPutAll(r));
                    } else if (r.containsKey("del")) {
                        deleteByUpdate (p.getFormRelatedCrud(), new RvaMap(req).rvaPutAll(r));
                    } else {
                        submitUpdateView(relatedApp.getUpdateId(), new RvaMap(req).rvaPutAll(r));
                    }
                }
            }
        });
    }

    private void deleteByUpdate(String appId, RvaMap req) {
        RvaApp app = rvaAppMapper.selectRvaAppById(appId);
        RvaView update = rvaViewMapper.selectRvaViewById(app.getUpdateId());
        RvaObject object = rvaObjectMapper.selectRvaObjectById(update.getObjId());
        Optional<RvaDeleteInterceptor> bean = RvaUtils.getBean(RvaDeleteInterceptor.BEAN_FREFIX + update.getId());
        update.getProperties().forEach(p -> {
            RvaProperty prop = object.getProperty(p.getPropId());
            if (prop == null) {
                return;
            }
            if (object.isPrimaryKey(prop.getName())) {
                String keyValue = req.getString(p.getId());
                bean.ifPresent(b -> {
                    if (b.preHandle(keyValue, null, object, update, req)) {
                        rvaDataMapper.deleteWhereMap(object.getId(), new RvaMap<>(prop.getName(), keyValue));
                    }
                });
                if (!bean.isPresent()) {
                    rvaDataMapper.deleteWhereMap(object.getId(), new RvaMap<>(prop.getName(), keyValue));
                }
                bean.ifPresent(b -> b.postHandle(keyValue, null, object, update, req));
            }
        });
    }

    private void setFieldValues(RvaMap req, RvaMap<String, Object> fieldValues, RvaViewproperty p, RvaProperty prop) {
        // FormSubmit == Y 时，才提交
        if ("N".equals(p.getFormSubmit())) {
            return;
        }
        // 根据FormSubmitValue替换提交值
        if (RvaUtils.isNotEmpty(p.getFormSubmitValue())) {
            String val = velocityUtils.parseWithLoginUser(p.getFormSubmitValue(), req);
            if (RvaUtils.isNotEmpty(req.get(p.getId()))) {
                if ("Y".equals(p.getFormSubmitReplace())) {
                    req.put(p.getId(), val);
                }
            } else {
                if ("Y".equals(p.getFormSubmitReplaceEmpty())) {
                    req.put(p.getId(), val);
                }
            }
        }
        if (p.hasMultipleValues()) {
            String join = RvaUtils.join(req.getList(p.getId()));
            if ("".equals(join)) {
                join = null;
            }
            fieldValues.put(prop.getName(), join);
        } else if (RvaUtils.isEmpty(p.getPropSubId())) {
            fieldValues.put(prop.getName(), req.getString(p.getId()));
        } else if (req.isNotEmpty(p.getId())) {
            Map map = (Map) fieldValues.get(prop.getName());
            if (map == null) {
                map = new HashMap();
            }
            map.put(p.getPropSubId(), req.getString(p.getId()));
            fieldValues.put(prop.getName(), map);
        }
    }

    @Override
    public RvaMap selectUpdateViewData(String viewId, RvaMap req, Boolean noDataThrow) {
        return selectUpdateViewData(viewId, req, false, noDataThrow);
    }

    private RvaMap selectUpdateViewData(String viewId, RvaMap req, Boolean clone, Boolean noDataThrow) {
        RvaView view = rvaViewMapper.selectRvaViewById(viewId);
        RvaObject object = rvaObjectMapper.selectRvaObjectById(view.getObjId());
        loadBeforeSql(req, view.getLoadBeforeSql());
        RvaSQL sql = new RvaSQL();
        sql.from(object.getId(), object.getNo());
        view.getProperties().forEach(p -> {
            RvaProperty prop = object.getProperty(p.getPropId());
            if (prop == null) {
                return;
            }
            sql.select(object.getNo(), prop.getName(), p.getPropSubId(), p.getId());
        });
        if (RvaUtils.isEmpty(view.getLoadWhere())) {
            throw new CustomException("load_where不能为空！");
        }
        String where = velocityUtils.parseWithLoginUser(view.getLoadWhere(), req);
        sql.where(where);
        List<RvaMap<String, Object>> formDataList = rvaDataMapper.selectList(sql.toString());
        if (formDataList.size() == 0) {
            if (noDataThrow) {
                throw new CustomException("未查询到数据！");
            }
            return new RvaMap("viewData", view).rvaPut("formDataList", formDataList);
        }
        view.getProperties().forEach(p -> {
            for (RvaMap<String, Object> formData : formDataList) {
                String val = formData.getString(p.getId());
                val = getValueByInit(val, req, p);
                formData.put(p.getId(), val);
                setFormRelatedCrudData(formData, p, req, false, clone);
                setFormMultipleValues(formData, p, val);
            }
        });
        loadAfterSql(req, view.getLoadAfterSql(), formDataList);
        return new RvaMap("viewData", view).rvaPut("formData", formDataList.get(0)).rvaPut("formDataList", formDataList);
    }

    @Override
    public void submitUpdateView(String viewId, RvaMap req) {
        RvaView update = rvaViewMapper.selectRvaViewById(viewId);
        RvaObject object = rvaObjectMapper.selectRvaObjectById(update.getObjId());
        RvaMap<String, Object> fieldValues = new RvaMap<>();
        RvaMap<String, Object> where = new RvaMap<>();
        Optional<RvaFormSubmitInterceptor> bean = RvaUtils.getBean(RvaFormSubmitInterceptor.BEAN_FREFIX + update.getId());
        bean.ifPresent(rvaFormSubmitInterceptor -> {
            rvaFormSubmitInterceptor.preHandle(fieldValues, object, update, req);
        });
        executeSubmitSql(req, update.getFormSubmitBeforeSql());
        update.getProperties().forEach(p -> {
            if ("Y".equals(p.getFormRequired()) && req.isEmpty(p.getId())) {
                throw new CustomException(String.format("【%s】为必需！", p.getName()));
            }
            RvaProperty prop = object.getProperty(p.getPropId());
            if (prop == null) {
                return;
            }
            if (object.isPrimaryKey(prop.getName())) {
                if (req.isEmpty(p.getId())) {
                    throw new CustomException("主键值不能为空！");
                }
                where.put(prop.getName(), req.getString(p.getId()));
            } else {
                setFieldValues(req, fieldValues, p, prop);
            }
        });
        checkUnique(object, new RvaMap<>(fieldValues).rvaPutAll(where), false);
        rvaDataMapper.updateWhereMap(object.getId(), fieldValues, where, true);
        updateIndexValue(req, object);
        processCrud(req, update);
        executeSubmitSql(req, update.getFormSubmitAfterSql());
        bean.ifPresent(rvaFormSubmitInterceptor -> {
            rvaFormSubmitInterceptor.postHandle(fieldValues, object, update, req);
        });
    }

    @Override
    public void delete(String viewId, RvaMap req) {
        if (req.isEmpty("selection")) {
            throw new CustomException("请至少选择一行数据！");
        }
        RvaView view = rvaViewMapper.selectRvaViewById(viewId);
        RvaObject object = rvaObjectMapper.selectRvaObjectById(view.getObjId());
        Optional<RvaDeleteInterceptor> bean = RvaUtils.getBean(RvaDeleteInterceptor.BEAN_FREFIX + view.getId());
        List<Map<String, Object>> selectionKeyValues = getSelectionValues(viewId, req, Arrays.asList(object.getPropNameKey()));
        for (int i = 0; i < selectionKeyValues.size(); i ++) {
            String keyValue = selectionKeyValues.get(i).get(object.getPropNameKey()).toString();
            Map selection = (Map) req.getList("selection").get(i);
            Map<String, Object> whereMap = new RvaMap<>();
            whereMap.put(object.getPropNameKey(), keyValue);
            bean.ifPresent(b -> {
                if (b.preHandle(keyValue, selection, object, view, req)) {
                    executeSubmitSql(new RvaMap(selection).rvaPutAll(req), view.getFormSubmitBeforeSql());
                    rvaDataMapper.deleteWhereMap(object.getId(), whereMap);
                }
            });
            if (!bean.isPresent()) {
                executeSubmitSql(new RvaMap(selection).rvaPutAll(req), view.getFormSubmitBeforeSql());
                rvaDataMapper.deleteWhereMap(object.getId(), whereMap);
            }
            // this.updateIndex (null, null, object, new RvaMap<>(selection));
            executeSubmitSql(new RvaMap(selection).rvaPutAll(req), view.getFormSubmitAfterSql());
            bean.ifPresent(b -> b.postHandle(keyValue,selection, object, view, req));
        }
    }

    private List<Map<String, Object>> getSelectionValues(String viewId, RvaMap rvaMap, List<String> propNames) {
        List<Map<String, Object>> values = new ArrayList<>();
        RvaView list = rvaViewMapper.selectRvaViewById(viewId);
        RvaObject object = rvaObjectMapper.selectRvaObjectById(list.getObjId());
        rvaMap.getList("selection").forEach(selection -> {
            Map<String, Object> value = new HashMap<>();
            values.add(value);
            list.getProperties().forEach(p -> {
                RvaProperty prop = object.getProperty(p.getPropId());
                if (prop == null) {
                    return;
                }
                if (propNames.contains(prop.getName())) {
                    value.put(prop.getName(), ((Map)selection).get(p.getId()));
                }
            });
        });
        return values;
    }

    @Override
    public void moveUp(String viewId, RvaMap req) {
        move (viewId, req, true);
    }

    private void move(String viewId, RvaMap rvaMap, Boolean moveUp) {
        if (rvaMap.getList("selection").size() < 1 || rvaMap.getList("selection").size() > 2) {
            throw new CustomException("请选择一行或两行数据！");
        }
        RvaView list = rvaViewMapper.selectRvaViewById(viewId);
        RvaObject object = rvaObjectMapper.selectRvaObjectById(list.getObjId());
        RvaMap<String, Object> data = object.parseData();
        if (object.hasNotPropIndex()) {
            throw new CustomException(String.format("%s 没有配置索引字段！", object.getName()));
        }
        List<String> propNames = new RvaList<String>(object.getPropNameKey(), object.getPropNameIndex())
                .rvaAddAll(object.getPropNameIndexWhere()); // 条件字段，propNameIndex对应的字段，是在条件字段在分组中进行排序的
        List<Map<String, Object>> selectionValues = getSelectionValues(viewId, rvaMap, propNames);
        Map<String, Object> selectionValue = selectionValues.get(0);
        List<RvaMap<String, Object>> selectList = getIndexList(object, propNames, selectionValue);
        if (selectionValues.size() == 1) {
            for (int i = 0; i < selectList.size(); i ++) {
                RvaMap<String, Object> row = selectList.get(i);
                if (row.getString(propNames.get(0)).equals(selectionValue.get(propNames.get(0)).toString())) {
                    if (i > 0 && moveUp) {
                        RvaMap<String, Object> row2 = moveUp(propNames, selectList, i, row);
                        updateRowIndex(object, propNames, row);
                        updateRowIndex(object, propNames, row2);
                        break;
                    }
                    if (i < selectList.size() - 1 && !moveUp) {
                        RvaMap<String, Object> row2 = moveDown(propNames, selectList, i, row);
                        updateRowIndex(object, propNames, row);
                        updateRowIndex(object, propNames, row2);
                        break;
                    }
                }
            }
        } else {
            Map<String, Object> selectionValue2 = selectionValues.get(1);
            int index = 0, index2 = 0;
            for (int i = 0; i < selectList.size(); i ++) {
                RvaMap<String, Object> row = selectList.get(i);
                String keyValue = row.getString(propNames.get(0));
                if (keyValue.equals(selectionValue.get(propNames.get(0)).toString())) {
                    index = i;
                }
                if (keyValue.equals(selectionValue2.get(propNames.get(0)).toString())) {
                    index2 = i;
                }
            }
            if (moveUp) {
                moveUpTo(selectList, index, index2);
            } else {
                moveDownTo(selectList, index, index2);
            }
            updateAllIndexes(object, propNames, selectList);
        }
    }

    private void updateAllIndexes(RvaObject object, List<String> propNames, List<RvaMap<String, Object>> selectList) {
        for (int i = 0; i < selectList.size(); i ++) {
            RvaMap<String, Object> row = selectList.get(i);
            row.put(propNames.get(1), i);
            updateRowIndex(object, propNames, row);
        }
    }

    /**
     *
     * @param propNames
     * @param selectList
     * @param i 当前行在selectList中的位置
     * @param row
     * @return
     */
    private RvaMap<String, Object> moveUp(List<String> propNames, List<RvaMap<String, Object>> selectList, int i, RvaMap<String, Object> row) {
        RvaMap<String, Object> prevRow = selectList.get(i - 1);
        String prevIndex = prevRow.get(propNames.get(1)).toString();
        prevRow.put(propNames.get(1), row.get(propNames.get(1)));
        row.put(propNames.get(1), prevIndex);
        return prevRow;
    }

    private RvaMap<String, Object> moveDown(List<String> propNames, List<RvaMap<String, Object>> selectList, int i, RvaMap<String, Object> row) {
        RvaMap<String, Object> nextRow = selectList.get(i + 1);
        String nextIndex = nextRow.get(propNames.get(1)).toString();
        nextRow.put(propNames.get(1), row.get(propNames.get(1)));
        row.put(propNames.get(1), nextIndex);
        return nextRow;
    }

    /**
     * selectList中位置Math.max(index, index2)的行，移动到Math.min(index, index2)的位置之前
     * @param selectList
     * @param index
     * @param index2
     */
    private void moveUpTo(List<RvaMap<String, Object>> selectList, int index, int index2) {
        int min = Math.min(index, index2);
        int max = Math.max(index, index2);
        RvaMap<String, Object> row = selectList.get(max);
        selectList.remove(max);
        selectList.add(min, row);
    }

    /**
     * selectList中位置Math.min(index, index2)的行，移动到Math.max(index, index2)的位置之后
     * @param selectList
     * @param index
     * @param index2
     */
    private void moveDownTo(List<RvaMap<String, Object>> selectList, int index, int index2) {
        int min = Math.min(index, index2);
        int max = Math.max(index, index2);
        RvaMap<String, Object> row = selectList.get(min);
        selectList.add(max + 1, row);
        selectList.remove(min);
    }

    private void updateIndex (String currentKeyValue, String currentIndexValue, RvaObject object, RvaMap<String, Object> selectionValue) {
        List<String> propNames = new RvaList<String>(object.getPropNameKey(), object.getPropNameIndex())
                .rvaAddAll(object.getPropNameIndexWhere()); // 条件字段，propNameIndex对应的字段，是在条件字段在分组中进行排序的
        List<RvaMap<String, Object>> indexList = getIndexList(object, propNames, selectionValue);
        Iterator<RvaMap<String, Object>> iterator = indexList.iterator();
        while (iterator.hasNext()) {
            if (iterator.next().getString(propNames.get(0)).equals(currentKeyValue)) {
                iterator.remove();
            }
        }
        if (RvaUtils.isNotEmpty(currentKeyValue)) {
            RvaMap current = new RvaMap(propNames.get(0), currentKeyValue);
            Integer currentIndex = RvaUtils.getInt(currentIndexValue, -1);
            if (currentIndex < 0 || indexList.size() == 0) {
                indexList.add(current);
            } else {
                int max = 0;
                for (int i = 0; i < indexList.size(); i++) {
                    RvaMap<String, Object> row = indexList.get(i);
                    if (currentIndex >= i) {
                        max = i;
                    }
                }
                if (currentIndex > max) {
                    indexList.add(current);
                } else {
                    indexList.add(max, current);
                }
            }
        }
        updateAllIndexes(object, propNames, indexList);
    }

    private List<RvaMap<String, Object>> getIndexList(RvaObject object, List<String> propNames, Map<String, Object> selectionVal) {
        RvaSQL sql = new RvaSQL();
        sql.from(object.getId(), object.getNo());
        sql.select(object.getNo(), propNames.get(0));// 主键
        sql.select(object.getNo(), propNames.get(1));// 索引列
        RvaMap selectionValue = new RvaMap(selectionVal);
        for (int i = 2; i < propNames.size(); i ++) {
            sql.where(String.format("%s.%s = '%s'", object.getNo(), propNames.get(i), selectionValue.get(propNames.get(i)).toString()));
        }
        sql.orderBy(String.format("%s.%s ASC", object.getNo(), propNames.get(1)));
        List<RvaMap<String, Object>> selectList = rvaDataMapper.selectList(sql.toString());
        return selectList;
    }

    private void updateRowIndex(RvaObject object, List<String> propNames, RvaMap<String, Object> row) {
        rvaDataMapper.updateWhereMap(object.getId(), new RvaMap<>(propNames.get(1), row.getString(propNames.get(1))),
                new RvaMap<>(propNames.get(0), row.getString(propNames.get(0))), true);
    }

    @Override
    public void moveDown(String viewId, RvaMap req) {
        move (viewId, req, false);
    }

    private final RvaAppMapper rvaAppMapper;
}
